<?php
/**
 * Unity (客户，店铺) 管理员登录认证
 *
 * Author: Zeon
 * Date: 2017/5/11
 * Time: 11:35
 * Created by PhpStorm.
 */
namespace App\Http\Controllers\PC;

use App\Exceptions\ApiErrCode;
use App\Exceptions\ApiException;
use App\Http\Controllers\ApiController;
use App\Http\Requests\PC\LoginPostRequest;
use App\Models\Admin\CustomerAdmin;
use App\Models\Admin\PanxAdmin;
use App\Models\Admin\StoreAdmin;
use App\Models\Customer\Customer;
use App\Models\Panx\PcSoftVersion;
use App\Models\Store\Store;
use App\Services\Locale\Language;
use App\Services\Utils\Guard;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Exceptions\JWTException;


class PcAuthController extends ApiController
{
    /**
     * 后台管理员的登录 职员的登录处理，返回token和角色id
     *
     * @param LoginPostRequest|Request $request
     *
     * @return array|\Illuminate\Http\JsonResponse
     * @throws ApiException
     * @throws JWTException
     */
    public function login(LoginPostRequest $request)
    {
        Language::setAppLocale($request->language);

        $admin = $this->findAdminByName($request);
            $this->checkItemExist($admin, trans('pc.admin'));
        $admin_ids = $this->listAdminIds($admin, $request);

        $token = $this->tryGenerateToken($request, $admin);
    
        // 返回管理员的角色和权限
        $roles = $admin->getRoleNames();

        $permissions = $admin->getAllPermissions();

        $pc_soft_version = PcSoftVersion::first();

        return $this->response(compact(
            'token','admin', 'roles', 'permissions', 'admin_ids' , 'pc_soft_version'
        ));
    }

    /**
     * @return array|\Illuminate\Http\Response
     * @throws ApiException
     */
    public function logout()
    {
        try {
            $logout = Auth::guard('admin')->logout();

            return $this->response(compact('token', 'logout'));
        } catch (\Exception $e) {
            throw New ApiException(ApiErrCode::TOKEN_ERROR, trans('pc.token'));
        }
    }
    
    /**
     * @param LoginPostRequest $request
     * @return PanxAdmin|null
     */
    private function findAdminByName(LoginPostRequest $request)
    {
        $account = $request->account;

        switch ($request->type) {
            case 'customer':
                $admin = $this->findCustomerAdmin($account);
                break;
            case 'store':
                $admin = $this->findStoreAdmin($account);
                break;

            default:
                $admin = false;
        }

        return $admin;
    }

    /**
     * @param LoginPostRequest $request
     * @param PanxAdmin|CustomerAdmin|StoreAdmin|Model $admin
     *
     * @return mixed
     * @throws ApiException
     * @throws JWTException
     */
    private function tryGenerateToken(LoginPostRequest $request, Model $admin)
    {
        $guard = $this->getGuardName($request->type);

        try {
            /**
             * 不能用 credentials ，JWT 0.5.* 不能实现 multi auth
             * attempt to verify the credentials and create a token for the user
             * @see https://github.com/tymondesigns/jwt-auth/issues/513#issuecomment-186087297
             */
            $token = Auth::guard($guard)->attempt([
                'account'  => $request->account,
                'password' => $request->password
            ]);
                $this->passwordVerifyCheck($token);
            
            // save token
            $admin->update(['remember_token' => $token]);

        } catch (JWTException $e) {
            if (config('app.debug')) throw $e;

            throw New ApiException(ApiErrCode::TOKEN_GENERATE_FAIL, trans('pc.token_error'));
        }
        
        return $token;
    }

    /**
     * @param $type
     * @return string
     */
    private function getGuardName(string $type): string
    {
        switch ($type) {
            case 'panx':
                $guard_name = Guard::ADMIN_PANX;
                break;
            case 'customer':
                $guard_name = Guard::ADMIN_CUSTOMER;
                break;
            case 'store':
                $guard_name = Guard::ADMIN_STORE;
                break;
            default:
                $guard_name = Guard::ADMIN_STORE;
        }

        return $guard_name;
    }

    /**
     * @param $account
     * @return mixed
     */
    private function findCustomerAdmin($account)
    {
        $admin = (new CustomerAdmin)
            ->where('is_deleted', CustomerAdmin::IS_NOT_DELETED)
            ->whereHas('customer', function ($query) {
                $query->where('is_deleted', Customer::IS_NOT_DELETED);
            })
            ->where('account', $account)
            ->with('customer')
            ->first();

        return $admin;
    }

    /**
     * @param $account
     * @return mixed
     */
    private function findStoreAdmin($account)
    {
        $admin = (new StoreAdmin)
            ->where('is_deleted', StoreAdmin::IS_NOT_DELETED)
            ->where('account', $account)
            ->whereHas('store', function ($query) {
                $query->where('is_deleted', Store::IS_NOT_DELETED);
            })
            ->whereHas('store', function ($query) {
                $query->whereHas('customer', function ($query) {
                    $query->where('is_deleted', Customer::IS_NOT_DELETED);
                });
            })
            ->first();

        return $admin;
    }

    /**
     * @param CustomerAdmin|StoreAdmin|Model $admin
     * @param $request
     * @return array
     */
    private function listAdminIds(Model $admin, LoginPostRequest $request): array
    {
        switch ($request->type) {
            case 'customer' :
                return $this->listCustomerAdminIds($admin);
            case 'store' :
                return $this->listStoreAdminIds($admin);
        }
    }

    /**
     * @param CustomerAdmin $admin
     * @return array
     */
    private function listCustomerAdminIds(CustomerAdmin $admin)
    {
        return [
            'customer_id' => $admin->customer_id,
            'store_id' => 0
        ];
    }

    /**
     * @param StoreAdmin $admin
     * @return array
     */
    private function listStoreAdminIds(StoreAdmin $admin)
    {
        return [
            'customer_id' => $admin->store->customer_id,
            'store_id' => $admin->store_id
        ];
    }

}